package com.sloth.bear.service;

import com.sloth.bear.config.FileManagerProperties;
import com.sloth.bear.util.FileUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 文件监控服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileWatcherService {

    private final FileIndexService fileIndexService;
    private final FileManagerProperties properties;

    private WatchService watchService;
    private final Map<WatchKey, Path> watchKeys = new HashMap<>();
    private ExecutorService executor;
    private volatile boolean running = false;
    
    // 事件节流相关
    private final Map<String, Long> fileEventThrottle = new HashMap<>();
    private static final long THROTTLE_INTERVAL = 5000; // 5秒内同一文件只处理一次

    @PostConstruct
    public void init() {
        // 异步初始化文件监控，避免阻塞应用启动
        CompletableFuture.runAsync(() -> {
            try {
                log.info("开始异步初始化文件监控服务...");
                watchService = FileSystems.getDefault().newWatchService();
                executor = Executors.newSingleThreadExecutor(r -> {
                    Thread t = new Thread(r, "file-watcher");
                    t.setDaemon(true);
                    return t;
                });

                startWatching();
                log.info("文件监控服务初始化完成");
            } catch (IOException e) {
                log.error("初始化文件监控服务失败", e);
            }
        }).exceptionally(throwable -> {
            log.error("异步初始化文件监控服务异常", throwable);
            return null;
        });
    }

    @PreDestroy
    public void destroy() {
        stopWatching();
        if (executor != null) {
            executor.shutdown();
        }
        if (watchService != null) {
            try {
                watchService.close();
            } catch (IOException e) {
                log.error("关闭文件监控服务失败", e);
            }
        }
    }

    /**
     * 开始监控
     */
    public void startWatching() {
        if (running || watchService == null) {
            return;
        }

        List<String> directories = properties.getScan().getDirectories();
        if (directories == null || directories.isEmpty()) {
            log.warn("未配置监控目录");
            return;
        }

        try {
            // 注册监控目录
            for (String directory : directories) {
                registerDirectory(Paths.get(directory));
            }

            running = true;

            // 启动监控线程
            executor.submit(this::watchForChanges);

            log.info("文件监控服务已启动，监控目录: {}", directories);
        } catch (Exception e) {
            log.error("启动文件监控失败", e);
        }
    }

    /**
     * 停止监控
     */
    public void stopWatching() {
        running = false;
        log.info("文件监控服务已停止");
    }

    /**
     * 注册目录监控（递归）
     */
    private void registerDirectory(Path dir) throws IOException {
        if (!Files.exists(dir) || !Files.isDirectory(dir)) {
            log.warn("目录不存在或不是有效目录: {}", dir);
            return;
        }

        // 注册当前目录
        WatchKey key = dir.register(watchService,
                StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_DELETE,
                StandardWatchEventKinds.ENTRY_MODIFY);
        watchKeys.put(key, dir);

        // 递归注册子目录（限制深度避免过度监控）
        Files.walk(dir, 2) // 只监控2层深度
                .filter(Files::isDirectory)
                .filter(path -> !path.equals(dir))
                .filter(path -> !isIgnoredDirectory(path)) // 忽略系统目录
                .forEach(subDir -> {
                    try {
                        registerDirectory(subDir);
                    } catch (IOException e) {
                        log.warn("注册子目录监控失败: {}", subDir, e);
                    }
                });
    }

    /**
     * 监控文件变化
     */
    private void watchForChanges() {
        while (running) {
            try {
                WatchKey key = watchService.take();
                Path dir = watchKeys.get(key);

                if (dir == null) {
                    continue;
                }

                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    if (kind == StandardWatchEventKinds.OVERFLOW) {
                        continue;
                    }

                    @SuppressWarnings("unchecked")
                    WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
                    Path filename = pathEvent.context();
                    Path fullPath = dir.resolve(filename);

                    handleFileEvent(kind, fullPath);
                }

                boolean valid = key.reset();
                if (!valid) {
                    watchKeys.remove(key);
                    log.warn("监控目录失效: {}", dir);
                }

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                log.error("处理文件监控事件失败", e);
            }
        }
    }

    /**
     * 处理文件事件
     */
    private void handleFileEvent(WatchEvent.Kind<?> kind, Path filePath) {
        try {
            String fileName = filePath.getFileName().toString();
            String absolutePath = filePath.toAbsolutePath().toString();
            
            // 事件节流：同一文件在短时间内只处理一次
            long currentTime = System.currentTimeMillis();
            Long lastProcessTime = fileEventThrottle.get(absolutePath);
            if (lastProcessTime != null && (currentTime - lastProcessTime) < THROTTLE_INTERVAL) {
                return; // 跳过频繁事件
            }
            fileEventThrottle.put(absolutePath, currentTime);
            
            // 清理过期的节流记录
            cleanupThrottleMap();

            if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
                handleFileCreate(filePath, fileName, absolutePath);
            } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                // 只有在启用删除监控时才处理删除事件
                if (properties.getScan().getEnableDeleteWatch()) {
                    handleFileDelete(absolutePath);
                } else {
                    log.debug("文件删除监控已禁用，忽略删除事件: {}", absolutePath);
                }
            } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                handleFileModify(filePath, fileName, absolutePath);
            }

        } catch (Exception e) {
            log.error("处理文件事件失败: {}", filePath, e);
        }
    }

    /**
     * 处理文件创建事件
     */
    private void handleFileCreate(Path filePath, String fileName, String absolutePath) {
        // 如果是目录，注册监控
        if (Files.isDirectory(filePath)) {
            try {
                registerDirectory(filePath);
                log.debug("注册新目录监控: {}", absolutePath);
            } catch (IOException e) {
                log.warn("注册新目录监控失败: {}", absolutePath, e);
            }
            return;
        }

        // 处理文件创建
        if (Files.isRegularFile(filePath)) {
            // 先检查文件类型，避免不必要的磁盘读取
            String fileExtension = FileUtils.getFileExtension(fileName);
            if (!properties.getScan().getSupportedFormats().contains(fileExtension)) {
                return; // 不支持的文件类型，直接跳过
            }
            
            try {
                long fileSize = Files.size(filePath);
                if (fileSize >= properties.getScan().getFileSizeThreshold().toBytes()) {
                    fileIndexService.addFileToIndex(absolutePath);
                    log.debug("检测到新文件: {}", absolutePath);
                }
            } catch (IOException e) {
                log.warn("获取文件大小失败: {}", absolutePath, e);
            }
        }
    }

    /**
     * 处理文件删除事件
     */
    private void handleFileDelete(String absolutePath) {
        fileIndexService.removeFileFromIndex(absolutePath);
        log.debug("检测到文件删除: {}", absolutePath);
    }

    /**
     * 处理文件修改事件
     */
    private void handleFileModify(Path filePath, String fileName, String absolutePath) {
        // 只处理文件修改，忽略目录修改
        if (Files.isRegularFile(filePath)) {
            // 先检查文件类型，避免不必要的磁盘读取
            String fileExtension = FileUtils.getFileExtension(fileName);
            if (!properties.getScan().getSupportedFormats().contains(fileExtension)) {
                return; // 不支持的文件类型，直接跳过
            }
            
            try {
                long fileSize = Files.size(filePath);
                if (fileSize >= properties.getScan().getFileSizeThreshold().toBytes()) {
                    fileIndexService.addFileToIndex(absolutePath);
                    log.debug("检测到文件修改: {}", absolutePath);
                }
            } catch (IOException e) {
                log.warn("获取文件大小失败: {}", absolutePath, e);
            }
        }
    }
    
    /**
     * 判断是否为需要忽略的目录
     */
    private boolean isIgnoredDirectory(Path path) {
        String dirName = path.getFileName().toString().toLowerCase();
        return dirName.startsWith(".") || // 隐藏目录
               dirName.equals("system volume information") || // Windows系统目录
               dirName.equals("$recycle.bin") || // 回收站
               dirName.equals("thumbs.db") || // 缩略图
               dirName.contains("temp") || // 临时目录
               dirName.contains("cache"); // 缓存目录
    }
    
    /**
     * 清理过期的节流记录
     */
    private void cleanupThrottleMap() {
        long currentTime = System.currentTimeMillis();
        fileEventThrottle.entrySet().removeIf(entry -> 
            (currentTime - entry.getValue()) > THROTTLE_INTERVAL * 2);
    }
}